import { MessagePort, parentPort } from "node:worker_threads";
import { WorkerMessage } from "./scan.js";
import { opendir } from "node:fs/promises";
import { Dir, Dirent } from "node:fs";
import EventEmitter from "node:events";
import { join } from "node:path";

(() => {
  let id = 0;
  let fileWalker: FileWalker;
  let tunnel: MessagePort;
  if (parentPort === null) {
    throw new Error("Worker 只能被 parent thread 启动，不能单独跑");
  }
  //   首先，我们监听父线程 message 消息，收到 startup 的消息的时候就创建 FileWorker 实例
  parentPort.on("message", (message: WorkerMessage) => {
    if (message.type === "startup") {
      id = message.value.id;
      tunnel = message.value.channel;
      fileWalker = new FileWalker();
      //   然后监听 port 发来的 scan 消息，调用 enqueueTask 加入任务队列。
      initTunnelListeners();
      //   并且监听 FileWorker 返回的 newResult，用 postMessage 传给父线程
      initFileWalkerListeners();
    }
  });
  function initTunnelListeners(): void {
    tunnel.on("message", (message: WorkerMessage) => {
      if (message?.type === "scan") {
        fileWalker.enqueueTask(message.value.path);
      }
    });
  }

  function initFileWalkerListeners(): void {
    fileWalker.events.on("newResult", ({ results }) => {
      tunnel.postMessage({
        type: "scanResult",
        value: { results },
      });
    });
  }
})();

interface Task {
  path: string;
}

class FileWalker {
  readonly events = new EventEmitter();
  private readonly taskQueue: Task[] = [];
  //   就是放入一个 task，然后循环取队列里的 task 来跑。
  enqueueTask(path: string) {
    this.taskQueue.push({ path });
    this.processQueue();
  }
  private processQueue() {
    while (this.taskQueue.length > 0) {
      const path = this.taskQueue.shift()?.path;
      if (path === undefined || path === "") {
        return;
      }
      this.run(path);
    }
  }
  private async run(path: string) {
    try {
      const dir = await opendir(path);
      await this.analizeDir(path, dir);
    } catch (error) {}
  }

  //    opendir，然后调用 dir.read 会遍历目录，把遍历到的每个目录都通过 newResult 返回
  private async analizeDir(path: string, dir: Dir) {
    const results: Array<Record<string, any>> = [];
    let entry: Dirent | null = null;
    while ((entry = await dir.read().catch(() => null)) != null) {
      this.newDirEntry(path, entry, results);
    }
    this.events.emit("newResult", { results });
    await dir.close();
  }

  private newDirEntry(path: string, entry: Dirent, results: any[]): void {
    const subpath = join(path, entry.name);
    const shouldSkip = !entry.isDirectory();
    // 如果不是目录就过滤，是目录的话判断下是不是 node_modules
    if (shouldSkip) {
      return;
    }
    results.push({
      path: subpath,
      isTarget: entry.name === "node_modules",
    });
  }
}
